#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import io
import pycdlib
from ..python3.wstage4._const import Arch, Version, Edition, Lang
from ..python3.wstage4._errors import InstallMediaError


class InstallMediaCustomizer:

    def __init__(self, src_iso_obj, target_iso_file):
        self._src = src_iso_obj
        self._target = target_iso_file

    def exists(self, udf_path=None, joliet_path=None, iso_path=None):
        # existence check, pycdlib doesn't support this function

        def __getAndCheck(rec, flen):
            if rec is None:
                if flen is not None:
                    raise InstallMediaError("invalid paths specified")
            else:
                if rec.is_dir():
                    if flen is not None:
                        if flen != -1:
                            raise InstallMediaError("invalid paths specified")
                    else:
                        flen = -1
                elif rec.is_file():
                    if flen is not None:
                        if flen != rec.get_data_length():
                            raise InstallMediaError("invalid paths specified")
                    else:
                        flen = rec.get_data_length()
                else:
                    raise InstallMediaError("invalid paths specified")
            return flen

        flen = None
        if udf_path is not None:
            try:
                rec = self._src.get_record(udf_path=udf_path)
                flen = __getAndCheck(rec, flen)
            except pycdlib.pycdlibexception.PyCdlibInvalidInput as e:
                # for udf_path, exception is raised if not found, strange
                if str(e) == 'Could not find path':
                    return False
                raise
        if joliet_path is not None:
            rec = self._src.get_record(joliet_path=joliet_path)
            flen = __getAndCheck(rec, flen)
        if iso_path is not None:
            rec = self._src.get_record(iso_path=iso_path)
            flen = __getAndCheck(rec, flen)

        return (flen is not None)

    def add_dir(self, udf_path=None, joliet_path=None, iso_path=None):
        self._src.add_directory(**self._getKwAll(udf_path, joliet_path, iso_path))

    def del_dir(self, udf_path=None, joliet_path=None, iso_path=None):
        # delete recursively, pycdlib doesn't support this function
        for child in self._src.list_children(**self._getKwOne(udf_path, joliet_path, iso_path)):
            if child.is_dir():
                if child is None or child.is_dot() or child.is_dotdot():
                    continue
                self.del_dir(**self._getKwNew(udf_path, joliet_path, iso_path, self._src.full_path_from_dirrecord(child)))
            elif child.is_file():
                self._src.rm_file(**self._getKwNew(udf_path, joliet_path, iso_path, self._src.full_path_from_dirrecord(child)))
            else:
                raise InstallMediaError("invalid record type")
        self._src.rm_directory(**self._getKwOne(udf_path, joliet_path, iso_path))

    def add_file(self, udf_path=None, joliet_path=None, iso_path=None, file_content=None):
        self._src.add_fp(io.BytesIO(file_content), len(file_content), **self._getKwAll(udf_path, joliet_path, iso_path))

    def del_file(self, udf_path=None, joliet_path=None, iso_path=None):
        self._src.rm_file(**self._getKwOne(udf_path, joliet_path, iso_path))

    def export_to_file(self):
        self._src.write(self._target)

    def dispose(self):
        del self._target
        del self._src

    def _getKwOne(self, udf_path, joliet_path, iso_path):
        if udf_path is not None:
            assert udf_path.startswith("/")
        if joliet_path is not None:
            assert joliet_path.startswith("/")
        if iso_path is not None:
            assert iso_path.startswith("/")
        if udf_path is not None:
            return {"udf_path": udf_path}
        if joliet_path is not None:
            return {"joliet_path": joliet_path}
        if iso_path is not None:
            return {"iso_path": iso_path}
        assert False

    def _getKwAll(self, udf_path, joliet_path, iso_path):
        ret = {}
        if udf_path is not None:
            assert udf_path.startswith("/")
            ret["udf_path"] = udf_path
        if joliet_path is not None:
            assert joliet_path.startswith("/")
            ret["joliet_path"] = joliet_path
        if iso_path is not None:
            assert iso_path.startswith("/")
            ret["iso_path"] = iso_path
        assert len(ret) > 0
        return ret

    def _getKwNew(self, udf_path, joliet_path, iso_path, path):
        assert path.startswith("/")
        if udf_path is not None:
            return {"udf_path": path}
        if joliet_path is not None:
            return {"joliet_path": path}
        if iso_path is not None:
            return {"iso_path": path}
        assert False





    def load_record(self, record_name, default_value=None):
        fullfn = os.path.join(self._path, record_name + ".save")
        if os.path.isfile(fullfn):
            return pathlib.Path(fullfn).read_text()
        else:
            return default_value

    def save_record(self, record_name, value):
        fullfn = os.path.join(self._path, record_name + ".save")
        with open(fullfn, "w") as f:
            f.write(value)

    def delete_record(self, record_name):
        fullfn = os.path.join(self._path, record_name + ".save")
        Util.forceDelete(fullfn)


    @staticmethod
    def winGetUser(userType="cur"):
        if userType == "cur":
            return "Administrator"
        elif userType == "default":
            return "Default User"
        elif userType == "all":
            return "All Users"
        elif userType == "admin":
            return "Administrator"
        else:
            assert False




        if False:
            # extract needed iso files
            subprocess.run(["7z", "x", "-o" + workDirObj.iso_op_dirpath, srcMedia.get_iso_filepath(), "SETUP.EXE", "WIN51", "WIN51IP", "WIN51IP.SP3", "I386", "[BOOT]/Boot-NoEmul.img"], stdout=subprocess.DEVNULL)
            if True:
                # make BOOT.IMG filename prettier
                os.rename(os.path.join(workDirObj.iso_op_dirpath, "[BOOT]/Boot-NoEmul.img"), os.path.join(workDirObj.iso_op_dirpath, "BOOT.IMG"))
                os.rmdir(os.path.join(workDirObj.iso_op_dirpath, "[BOOT]"))

            # add answer file
            answerFile = AnswerFile(ts)
            with open(os.path.join(workDirObj.iso_op_dirpath, answerFile.get_filename()), "wb") as f:
                f.write(answerFile.get_binary_content())

            # create real install iso
            iso = pycdlib.PyCdlib()
            iso.new(joliet=3, interchange_level=4)    # specify interchange_level because files in winxp installation iso do not comply with ISO9660 spec strictly
            try:
                for root, dirs, files in os.walk(workDirObj.iso_op_dirpath):
                    root2 = root.replace(workDirObj.iso_op_dirpath, "")
                    if root2 == "":
                        root2 = "/"
                    for dn in dirs:
                        print(os.path.join(root2, dn))
                        Util.pycdlibAddDirectory(iso, joliet_path=os.path.join(root2, dn))
                    for fn in files:
                        Util.pycdlibAddFile(iso, pathlib.Path(os.path.join(root, fn)).read_bytes(), joliet_path=os.path.join(root2, fn))
                iso.add_eltorito(bootfile_path="/BOOT.IMG", media_name="noemul")
                iso.write(workDirObj.installation_iso_filepath)
            finally:
                iso.close()


            if ts.edition == Edition.WINDOWS_XP_HOME:
                assert False
            elif ts.edition == Edition.WINDOWS_XP_PROFESSIONAL:
                vuser.operate(vuser.InitConfirmXpProfessional())
            else:
                assert False
            vuser.operate(vuser.InitLicenseConfirm())
            vuser.operate(vuser.InitPartitionSelect())
            vuser.operate(vuser.InitPartitionFormat())          # FIXME: this two is used to make partition bootable, we should do that in linux
            vuser.operate(vuser.InitPartitionFormatConfirm())



    def use_default_product_key(self):
        assert self.product_key is None

        # these are the "public knowledge" abandonware key supplied by Microsoft
        # there may be additional logic in future, so we don't use dict here

        if self.edition == Edition.WINDOWS_7_STARTER:
            # from https://www.windowsafg.com/keys.html
            return "7Q28W-FT9PC-CMMYT-WHMY2-89M6G"

        if self.edition == Edition.WINDOWS_7_HOME_BASIC:
            # from https://www.windowsafg.com/keys.html
            return "YGFVB-QTFXQ-3H233-PTWTJ-YRYRV"

        if self.edition == Edition.WINDOWS_7_HOME_PREMIUM:
            # from https://www.windowsafg.com/keys.html
            return "RHPQ2-RMFJH-74XYM-BH4JX-XM76F"

        if self.edition == Edition.WINDOWS_7_PROFESSIONAL:
            # from https://www.windowsafg.com/keys.html
            return "HYF8J-CVRMY-CM74G-RPHKF-PW487"

        if self.edition == Edition.WINDOWS_7_ULTIMATE:
            # from https://www.windowsafg.com/keys.html
            return "D4F6K-QK3RD-TMVMJ-BBMRX-3MBMV"

        if self.edition == Edition.WINDOWS_7_ENTERPRISE:
            # from https://www.windowsafg.com/keys.html
            return "H7X92-3VPBB-Q799D-Y6JJ3-86WC6"

        assert False





# @Action(BuildStep.OS_INSTALLED, BuildStep.CORE_APPS_INSTALLED, BuildStep.EXTRA_APPS_INSTALLED)
# def action_customize_system(self, custom_script_list=[]):
#     assert all([isinstance(s, ScriptInChroot) for s in custom_script_list])

#     if len(custom_script_list) == 0:
#         return

#     spec = None
#     if self._ts.version == Version.WINDOWS_98:
#         spec = os_win98.Spec()
#     elif self._ts.version == Version.WINDOWS_XP:
#         spec = os_winxp.Spec(self._ts.arch)
#     else:
#         assert False

#     vmDisplayRecordFile = os.path.join(self._workDirObj.path, "record-customize.mp4")
#     vm = Vm(spec.getVmConfigFinal(),
#             self._workDirObj.image_filepath,
#             self._workDirObj.vm_display_socket_filepath, vmDisplayRecordFile,
#             self._workDirObj.vm_control_socket_filepath)
#     vm.start(show=not self._getQuiet())
#     try:
#         for s in custom_script_list:
#             vm.script_exec(s, quiet=self._getQuiet())
#         vm.wait_until_stop()
#     except BaseException:
#         vm.stop()
#         raise


           # it seems there's no good virtio-blk driver for winxp 64-bit

        # buf += 'Command1="ping -n 120"\n'               # wait about 2 minutes for NTP synchronization, shutdown's timeout malfunctions if system time change
        # buf += 'Command2="shutdown /s /f /t 0"\n'



        vm.use_floppy(0, workDirObj.setup_driver_floppy_filepath)       # windows setup automatically picks up viostor from this floppy, this driver is fast



    class RestartWhenStuck:

        def __init__(self):
            self._lastImg = None

        def do(self, parent, img):
            # test if the left side of screen (contains progress bar) has stuck for 5 minutes
            if self._lastImg is None:
                self._lastImg = img.crop((0, 0, 175, img.size[1]))
                self._stableCount = 0
                return False
            elif self._lastImg != img.crop((0, 0, 175, img.size[1])):
                self._lastImg = None
                self._stableCount = None
                return False
            elif self._stableCount < 5 * 60:
                self._stableCount += 1
                return False

            parent._vm.poweroff()
            return True


class CustomActionExecCommands(CustomAction):

    @property
    @abc.abstractmethod
    def commands(self):
        pass

    def modify_work_dir(self, action_name, s, ts, work_dir):
        key = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"

        with _ImageFileMount(work_dir) as mp:
            # add runonce registry entry
            reg = WinRegistry(mp)
            valueName = "run%d" % (len(reg.get_value_name_list(key)))
            assert not reg.exists(key, valueName, "REG_SZ")
            reg.add_or_modify(key, valueName, "REG_SZ", 'C:\\%s.bat' % (valueName))

            # put batch files into virtual machine disk
            with open(os.path.join(mp, "%s.bat" % (valueName)), "wb") as f:
                f.write(_BatchFile(self.commands).get_binary_content())

    def run(self, action_name, s, ts, work_dir):
        spec = Spec(Arch.X86)

        # run virtual machine
        vm = Vm(work_dir, spec.getVmConfigFinal(), "%s.mp4" % (action_name))
        vm.start(show=(s.verbose_level > 0))
        try:
            vuser = _VirtualUser("eng", vm)
            vuser.operate(vuser.WaitStartMenu())
            # FIXME: should see the pause and press key
        except BaseException:
            vm.stop()
            raise

        return vm

    def run_powershell_command(self, command):
        return self.a.execute(
            f'powershell -ExecutionPolicy bypass "{command}"'
        )

    def run_powershell_strings(self, powershell_strings):
        script_winpath = f"c:\\{random_string(6, 10)}.ps1"
        self.a.upload(script_winpath, powershell_strings)
        try:
            return self.a.execute(
                f"powershell -ExecutionPolicy bypass -File {script_winpath}"
            )
        finally:
            self.a.remove(script_winpath)

    def run_powershell_file(self, powershell_file):
        script_winpath = f"c:\\{random_string(6, 10)}.ps1"
        self.upload_file(powershell_file, script_winpath)
        try:
            return self.a.execute(
                f"powershell -ExecutionPolicy bypass -File {script_winpath}"
            )
        finally:
            self.a.remove(script_winpath)





def valid_serial_key(serial_key):
    """Determines whether `serial_key` has a valid encoding."""
    parts = serial_key.split("-")
    if len(parts) != 5:
        return False

    for part in parts:
        if not re.match("[A-Z0-9]{5}$", part):
            return False

    return True

def valid_keyboard_layout(kblayout):
    kblayout_txt = os.path.join(VMCLOAK_ROOT, "data", "winxp",
                                "keyboard_layout_values.txt")
    for layout in open(kblayout_txt, "rb"):
        if layout.strip() == kblayout:
            return True






class ACPIShutdown(Dependency):
    """Tells Windows that to do if an ACPI shutdown signal (power button) is
    sent. These settings tell Windows to shut down."""
    name = "acpishutdown"

    def run(self):
        # Tell Windows to shut down if the power button is clicked when it
        # is running on the battery
        self.a.execute(
            "c:\\Windows\\System32\\powercfg.exe "
            "-setdcvalueindex SCHEME_CURRENT "
            "4f971e89-eebd-4455-a8de-9e59040e7347 "
            "7648efa3-dd9c-4e3e-b566-50f929386280 3"
        )
        # Tell Windows to shut down if the power button is clicked when it is
        # not running from the battery
        self.a.execute(
            "c:\\Windows\\System32\\powercfg.exe "
            "-setacvalueindex SCHEME_CURRENT "
            "4f971e89-eebd-4455-a8de-9e59040e7347 "
            "7648efa3-dd9c-4e3e-b566-50f929386280 3"
        )





        # prepare for the final install stage
        # with UtilWin7.mountImageFile(workDirObj) as mp:
        #     guiRunOnceBatchFile = GuiRunOnceBatchFile()

        #     # install virtio drivers
        #     if "virtio" in ts.extra_drivers:
        #         if ts.arch == Arch.X86:
        #             guiRunOnceBatchFile.add_command(r'pnputil.exe /add-driver D:\Balloon\w7\x86\balloon.inf')
        #         elif ts.arch == Arch.X86_64:
        #             guiRunOnceBatchFile.add_command(r'pnputil.exe /add-driver D:\Balloon\w7\amd64\balloon.inf')
        #         else:
        #             assert False
        #         if ts.arch == Arch.X86:
        #             guiRunOnceBatchFile.add_command(r'pnputil.exe /add-driver D:\NetKVM\w7\x86\netkvm.inf')
        #         elif ts.arch == Arch.X86_64:
        #             guiRunOnceBatchFile.add_command(r'pnputil.exe /add-driver D:\NetKVM\w7\amd64\netkvm.inf')
        #         else:
        #             assert False
        #         if ts.arch == Arch.X86:
        #             guiRunOnceBatchFile.add_command(r'pnputil.exe /add-driver D:\i386\vioscsi.inf')
        #         elif ts.arch == Arch.X86_64:
        #             guiRunOnceBatchFile.add_command(r'pnputil.exe /add-driver D:\amd64\vioscsi.inf')
        #         else:
        #             assert False

        #     # $Installer = "qemu-ga-x86_64.msi"
        #     # if ([Environment]::Is64BitOperatingSystem -eq $false)
        #     # { 
        #     #     $Installer = "qemu-ga-i386.msi"
        #     # }
        #     # Start-Process msiexec -ArgumentList "/I e:\GUEST-AGENT\$Installer /qn /norestart" -Wait -NoNewWindow

        #     with open(os.path.join(mp.drive_c_dir, guiRunOnceBatchFile.get_filename()), "wb") as f:
        #         f.write(guiRunOnceBatchFile.get_binary_content())

            # reg = WinRegistry(mp)
            # reg.add_or_modify("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", "MyEntry", "REG_SZ", r'%systemroot%\system32\msiexec.exe /i "D:\guest-agent\qemu-ga-i386.msi"')

        # # install-guest-agent
        # ret = workDirObj.mount_image_file()
        # try:
        #     assert len(ret) == 1
        #     mp = list(ret.values())[0]
        #     reg = WinRegistry(mp)
        #     reg.add_or_modify("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", "MyEntry", "REG_SZ", r'%systemroot%\system32\msiexec.exe /i "D:\guest-agent\qemu-ga-i386.msi"')
        # finally:
        #     workDirObj.umount_image_file()



        # FIXME
        # <settings pass="specialize">
        #     <component name="Microsoft-Windows-Shell-Setup" @@component_tag_postfix@@>
        #         <ShowWindowsLive>false</ShowWindowsLive>


        # <settings pass="specialize">
        #     <component name="Microsoft-Windows-Deployment" processorArchitecture="@ARCH@" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        #         <RunSynchronous>
        #             <RunSynchronousCommand wcm:action="add">
        #                 <Order>1</Order>
        #                 <Path>reg add HKLM\SOFTWARE\CurrentControlSet\Control\Network\NewNetworkWindowOff</Path>
        #                 <WillReboot>Never</WillReboot>
        #             </RunSynchronousCommand>
        #             <RunSynchronousCommand wcm:action="add">
        #                 <Order>2</Order>
        #                 <Path>reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce /v !Bootstrap /t REG_SZ /d C:\vmcloak\bootstrap.bat</Path>
        #                 <WillReboot>Never</WillReboot>
        #             </RunSynchronousCommand>
        #         </RunSynchronous>
        #     </component>
        #     <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="@ARCH@" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        #         <SkipAutoActivation>true</SkipAutoActivation>
        #     </component>
        # <component name="Security-Malware-Windows-Defender" processorArchitecture="@ARCH@" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        #     <DisableAntiSpyware>true</DisableAntiSpyware>
        # </component>
        # <component name="Networking-MPSSVC-Svc" processorArchitecture="@ARCH@" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        #     <DisableStatefulFTP>true</DisableStatefulFTP>
        #     <DisableStatefulPPTP>true</DisableStatefulPPTP>
        #     <DomainProfile_EnableFirewall>false</DomainProfile_EnableFirewall>
        #     <PrivateProfile_EnableFirewall>false</PrivateProfile_EnableFirewall>
        #     <PublicProfile_EnableFirewall>false</PublicProfile_EnableFirewall>
        # </component>

        # <settings pass="oobeSystem">                                            <!-- system Out-Of-Box-Experience -->
        #    <component name="Microsoft-Windows-Shell-Setup" @@component_tag_postfix@@>
        #        <FirstLogonCommands>
        #             <SynchronousCommand>
        #                 <Order>1</Order>
        #                 <CommandLine>shutdown /s /t 60</CommandLine>
        #             </SynchronousCommand>
        #         </FirstLogonCommands>
 
        # <component name="Microsoft-Windows-Setup" @@component_tag_postfix@@>
            # <DynamicUpdate>
            #     <Enable>false</Enable>
            #     <WillShowUI>OnError</WillShowUI>
            # </DynamicUpdate>
            # <EnableNetwork>true</EnableNetwork>
            # <EnableFirewall>false</EnableFirewall>
        #

        # <component name="Microsoft-Windows-TCPIP" processorArchitecture="@ARCH@" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        #     <Interfaces>
        #         <Interface wcm:action="add">
        #             <Identifier>@INTERFACE@</Identifier>
        #             <Ipv4Settings>
        #                 <DhcpEnabled>false</DhcpEnabled>
        #                 <Metric>1</Metric>
        #                 <RouterDiscoveryEnabled>false</RouterDiscoveryEnabled>
        #             </Ipv4Settings>
        #             <Ipv6Settings>
        #                 <DhcpEnabled>false</DhcpEnabled>
        #                 <Metric>30</Metric>
        #                 <RouterDiscoveryEnabled>true</RouterDiscoveryEnabled>
        #             </Ipv6Settings>
        #             <UnicastIpAddresses>
        #                 <IpAddress wcm:action="add" wcm:keyValue="1">@IPADDRESS@</IpAddress>
        #             </UnicastIpAddresses>
        #             <Routes>
        #                 <Route wcm:action="add">
        #                     <Identifier>0</Identifier>
        #                     <Prefix>0.0.0.0/0</Prefix>
        #                     <NextHopAddress>@DEFAULTGATEWAY@</NextHopAddress>
        #                 </Route>
        #             </Routes>
        #         </Interface>
        #     </Interfaces>
        # </component>

        # <settings pass="offlineServicing">
        #     <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="@ARCH@" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        #         <EnableLUA>false</EnableLUA>
        #     </component>
        # </settings>

            # <UserAccounts>
                # <AdministratorPassword>
                #     <Value>cuckoo</Value>
                #     <PlainText>true</PlainText>
                # </AdministratorPassword>
            #

                    # <WillWipeDisk>true</WillWipeDisk>
                    # <CreatePartitions>
                    #     <CreatePartition>                                                       <!-- system reserved partition -->
                    #         <Order>1</Order>
                    #         <Type>Primary</Type>
                    #         <Size>100</Size>
                    #     </CreatePartition>
                    #     <CreatePartition>                                                       <!-- windows partition -->
                    #         <Order>2</Order>
                    #         <Type>Primary</Type>
                    #         <Extend>true</Extend>                                               <!-- use all remaining space -->
                    #     </CreatePartition>
                    # </CreatePartitions>
                    # <ModifyPartitions>
                    #     <ModifyPartition>
                    #         <Order>1</Order>
                    #         <PartitionID>1</PartitionID>
                    #         <Active>true</Active>
                    #         <Format>NTFS</Format>
                    #         <Label>System Reserved</Label>
                    #     </ModifyPartition>
                    #     <ModifyPartition>
                    #         <Order>2</Order>
                    #         <PartitionID>2</PartitionID>
                    #         <Format>NTFS</Format>
                    #         <Letter>C</Letter>
                    #     </ModifyPartition>
                    # </ModifyPartitions>



# class InstallLanguagePack:
#    pass

# class SetPasswordForAdministrator:

#     def __init__(self, password):
#         self._hash = crypt.crypt(password)


# class AddUser:

#     def __init__(self, username, password, comment=""):
#         self._user = username
#         self._pwd = password
#         self._comment = comment

#     def update_custom_script_list(self, custom_script_list):
#         assert False



class SetPasswordForAdministrator:

    def __init__(self, password):
        self._hash = crypt.crypt(password)

    def update_custom_script_list(self, custom_script_list):
        buf = ""
        buf += "#!/bin/sh\n"
        buf += "sed -i 's#^root:[^:]*:#root:%s:#' /etc/shadow\n" % (self._hash)      # modify /etc/shadow directly so that password complexity check won't be in our way

        s = ScriptFromBuffer("Set root's password", buf)
        assert s not in custom_script_list
        custom_script_list.append(s)


class AddUser:

    def __init__(self, username, password, comment=""):
        self._user = username
        self._pwd = password
        self._comment = comment

    def update_custom_script_list(self, custom_script_list):
        assert False


            action.modify_work_dir(action_name, self._s, self._ts, self._workDirObj)

            # we only call this method if it is overrided (save resource)
            if action.modify_drive_c != CustomAction.modify_drive_c:
                mp = self._workDirObj.mount_image_file()
                try:
                    if self._ts.version == Version.WINDOWS_98:
                        cPath = mp.mnt_dir_0
                    elif self._ts.version == Version.WINDOWS_XP:
                        cPath = mp.mnt_dir_0
                    elif self._ts.version == Version.WINDOWS_7:
                        cPath = mp.mnt_dir_0
                    else:
                        assert False
                    action.modify_drive_c(action_name, self._s, self._ts, cPath)
                finally:
                    self._workDirObj.umount_image_file()






"""
  # Do not hide folder merge conflicts
  # Не скрывать конфликт слияния папок
  ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','HideMergeConflicts',0),

  # Open File Explorer to: "This PC"
  # Открывать проводник для: "Этот компьютер"
  ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','LaunchTo',1),

  # Do not show all folders in the navigation pane
  # Не отображать все папки в области навигации
  ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','NavPaneShowAllFolders',0),

  # Do not show Cortana button on taskbar
  # Не показывать кнопку Кортаны на панели задач
  ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','ShowCortanaButton',0),

  # Do not show sync provider notification within File Explorer
  # Не показывать уведомления поставщика синхронизации в проводнике
  ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','ShowSyncProviderNotifications',0),

  # Do not show Task View button on taskbar
  # Не показывать кнопку Просмотра задач
  ('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','ShowTaskViewButton',0)

# Do not use check boxes to select items
('HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced','AutoCheckSelect',0),

# Do not offer tailored experiences based on the diagnostic data setting
('HKCU:\Software\Microsoft\Windows\CurrentVersion\Privacy','TailoredExperiencesWithDiagnosticDataEnabled',0),

<!-- [TASKBAR] Task View button (0 = Hide)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowTaskViewButton" /t REG_DWORD /d 0 /f

<!-- [TASKBAR] Set alignment to Left (0 = Left) -->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarAl" /t REG_DWORD /d 0 /f

<!-- [TASKBAR] widgets  (0 = Disable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarDa" /t REG_DWORD /d 0 /f

<!-- [TASKBAR] Copilot button on taskbar (0 = Disable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowCopilotButton" /t REG_DWORD /d 0 /f

<!-- [TASKBAR] Search box (0 = Hide)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Search" /v "SearchboxTaskbarMode" /t REG_DWORD /d 0 /f

<!-- [EXPLORER] Compact view for items (1 = Enable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "UseCompactMode" /t REG_DWORD /d 1 /f

<!-- [EXPLORER] Bing Search in start menu (0 = Disable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Search" /v "BingSearchEnabled" /t REG_DWORD /d 0 /f

<!-- [TASKBAR] Disable App Launch Tracking for all users (1 = Enable)-->
reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\EdgeUI" /v "DisableMFUTracking" /t REG_DWORD /d 1 /f

<!-- [SUGGESTIONS] Show recommendations for tips, shortcuts, new apps, and more in start (0 = Disable) -->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "Start_IrisRecommendations" /t REG_DWORD /d 0 /f

<!-- [SUGGESTIONS] Suggest ways I can finish setting up my device to get the most out of Windows (0 = Disable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\UserProfileEngagement" /v "ScoobeSystemSettingEnabled" /t REG_DWORD /d 0 /f

<!-- [SUGGESTIONS] Sync provider ads (0 = Disable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowSyncProviderNotifications" /t REG_DWORD /d 0 /f

<!-- [SUGGESTIONS] "Suggested" app notifications (Ads for MS services) (0 = Disable)-->
reg.exe add "HKU\DefaultUser\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\Windows.SystemToast.Suggested" /v "Enabled" /t REG_DWORD /d 0 /f

reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v LaunchTo /t REG_DWORD /d 1 /f


<!-- [EXPLORER] Add "Compress to..." to old context menu for files -->
reg.exe add "HKEY_CLASSES_ROOT\*\shell\CompressToFullMenu_ForOldContextMenu" /v "CommandStateSync" /t REG_SZ /d "" /f
reg.exe add "HKEY_CLASSES_ROOT\*\shell\CompressToFullMenu_ForOldContextMenu" /v "ExplorerCommandHandler" /t REG_SZ /d "{7AE6900F-6EB0-44A2-9CA1-DB2F7EF352AF}" /f
reg.exe add "HKEY_CLASSES_ROOT\*\shell\CompressToFullMenu_ForOldContextMenu" /v "MUIVerb" /t REG_SZ /d "@Windows.UI.FileExplorer.dll,-51797" /f

<!-- [EXPLORER] Add "Compress to..." to old context menu for folders -->
reg.exe add "HKEY_CLASSES_ROOT\Folder\shell\CompressToFullMenu_ForOldContextMenu" /v "CommandStateSync" /t REG_SZ /d "" /f
reg.exe add "HKEY_CLASSES_ROOT\Folder\shell\CompressToFullMenu_ForOldContextMenu" /v "ExplorerCommandHandler" /t REG_SZ /d "{7AE6900F-6EB0-44A2-9CA1-DB2F7EF352AF}" /f
reg.exe add "HKEY_CLASSES_ROOT\Folder\shell\CompressToFullMenu_ForOldContextMenu" /v "MUIVerb" /t REG_SZ /d "@Windows.UI.FileExplorer.dll,-51797" /f


Open File Explorer to This PC
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced] "LaunchTo"=dword:0000000
1 for This PC, 2 for Quick Access

Show recent used files in Quick access
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer] "ShowRecent"=dword:00000000
0 for off, 1 for on.

Show frequently used folders in Quick access
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer] "ShowFrequent"=dword:00000000
0 for off, 1 for on.

"""
